#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "$"
#define DELIM " "

#define NONE -1
#define IN_RDIR     0
#define OUT_RDIR    1
#define APPEND_RDIR 2

#define LINE_SIZE 32

char commandline[LINE_SIZE];//输入的命令行
char* argv[LINE_SIZE];//分割出来的命令行参数
char pwd[LINE_SIZE];//当前所在目录
int exitcode=0;//保存退出码

int rdir=NONE;//是否为重定向
char* filename=NULL;//重定向的文件名


//环境变量表
char  myenv[LINE_SIZE];

char* getusername()
{
    return getenv("USER");
}

char* gethostname()
{
    return getenv("HOSTNAME");
}

char* getpwd()
{
    return getcwd(pwd,sizeof(pwd));
}

void check_redir()
{
    char* pos=commandline;
    while(*pos)
    {
        if(*pos=='>')
        {
            if(*(pos+1)=='>')
            {
                *pos++='\0';
                *pos++='\0';
                while(isspace(*pos)) pos++;
                filename=pos;
                rdir=APPEND_RDIR;
                break;
            }
            else
            {
                *pos++='\0';
                while(isspace(*pos)) pos++;
                filename=pos;
                rdir=OUT_RDIR;
                break;
            }
        }
        else if(*pos=='<')
        { 
            *pos++='\0';
             while(isspace(*pos)) pos++;
             filename=pos;
             rdir=IN_RDIR;
             break;
        }
        else
        {
            //do nothing
        }

        pos++;
    }
}

void interactive(int size)
{  
    getpwd();
    printf(LEFT"%s@%s %s"RIGHT"%s"" ",getusername(),gethostname(),pwd,LABLE);
    fgets(commandline,size,stdin);
    //abcde\n\0
    commandline[strlen(commandline)-1]='\0';

    //检查是否有重定向
    check_redir();
}

int splitstring()
{       //解决一下当命令行只输入回车时的段错误
        if(strlen(commandline)==0)
            return 0;

        int i=0;
        argv[i++]=strtok(commandline,DELIM);
        while(argv[i++]=strtok(NULL,DELIM));

        //for(i=0;argv[i];i++)
        //   printf("%s\n",argv[i]);

        return i-1;//返回分割出字串的个数
}

void ordinarycommands()
{
     pid_t id=fork();
     if(id==0)
     {
         if(rdir==OUT_RDIR)
         {
            int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
            if(fd<0)
            {
                perror("open");
                exit(1);
            }

            dup2(fd,1);
         }
         else if(rdir==APPEND_RDIR)
         {
            int fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
            if(fd<0)
            {
                perror("open");
                exit(1);
            }

            dup2(fd,1);
         }
         else if(rdir==IN_RDIR)
         {
            int fd=open(filename,O_RDONLY);
            if(fd<0)
            {
                perror("open");
                exit(1);
            }

            dup2(fd,0);
         }

         execvp(argv[0],argv);
         exit(-1);
     }
     else if(id>0)
     {
         int status=0;
         pid_t rid=waitpid(id,&status,0);
         if(rid==id)
         {
             exitcode=WEXITSTATUS(status);
             //printf("wait %d success\n",id);
         }
         else if(rid<0)
         {
             perror("wait fali");
         }
     }
     else
     {
         perror("fork fail");
     }   
}

int buildcommand(int _argc)
{
    if(_argc==2 && strcmp(argv[0],"cd")==0)
    {
        chdir(argv[1]);

        return 1;
    }
    else if(_argc==2 && strcmp(argv[0],"export")==0)
    {
        //putenv不是把argv[1]这个值导入进去  而是有一个环境变量表(指针数组) 这个指针数组里找一个空位指向这个argv[1] 所以这个argv的值是不能随意改变的
        //putenv(argv[1]);
        
        strcpy(myenv,argv[1]);
        putenv(myenv);

        return 1;
    }
    else if(_argc==2 && strcmp(argv[0],"echo")==0)
    {
        if(strcmp(argv[1],"$?")==0)
        {
           printf("%d\n",exitcode);

           //退出码设置为0 应为执行echo $?后退出码为bach这次进程结束的退出码
           exitcode=0;
        }
        else if(*argv[1]=='$')
        {
            char* val=getenv((argv[1]+1));
            if(val) printf("%s\n",val);
        }
        else 
        {
            printf("%s\n",argv[1]);
        }

        return 1;
    }


    //特殊处理一下ls的颜色输出
    if(strcmp(argv[0],"ls")==0)
    {
        argv[_argc++]="--color";
        argv[_argc]=NULL;

        return 0;
    }

    //不属于内建命令
    return 0;
}


int main()
{
    while(1)
    {
        //每次还原重定向参数
        rdir=NONE;
        filename=NULL;

        interactive(sizeof(commandline));

        int argc= splitstring();
        if(argc==0) continue;
        
        int n=buildcommand(argc);

        if(!n) ordinarycommands();
    }
    return 0;
}
